﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using System.Runtime.InteropServices;

namespace LowLevelGraphics.Filter
{
    /// <summary>
    /// Clustering algorithm
    /// </summary>
    public class Clustering : BaseImageFilter
    {
        /// <summary>
        /// Initializes a new instance of the <see cref="Clustering"/> class.
        /// </summary>
        public Clustering()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Clustering"/> class.
        /// </summary>
        /// <param name="_clustering">The _clustering.</param>
        internal Clustering(Clustering _clustering)
        {
        }

        /// <summary>
        /// Execute this Filter
        /// A Debug Statement is written if a concrete Filter is called
        /// </summary>
        /// <param name="_bitmap"></param>
        /// <returns></returns>
        public override UnsafeBitmap Execute(UnsafeBitmap _bitmap)
        {
            UnsafeBitmap currentBitmap = _bitmap;
            Rectangle rect = new Rectangle(0, 0, currentBitmap.Width, currentBitmap.Height);
            BitmapData bmpData = currentBitmap.BitmapData; //currentBitmap.LockBits(rect, ImageLockMode.ReadWrite, currentBitmap.PixelFormat);
            IntPtr ptr = bmpData.Scan0;
            int aByte = currentBitmap.Width * currentBitmap.Height;
            byte[] aGrayValues = new byte[aByte];
            System.Runtime.InteropServices.Marshal.Copy(ptr, aGrayValues, 0, aByte);

            //TODO
            bool bMethod = true; //cluMethod.GetMethod;
            int nNumbers = 1; // cluMethod.GetNumber;

            if (bMethod == false)//k-mean
            {
                int[] kNum = new int[nNumbers];
                int[] kAver = new int[nNumbers];
                int[] kOldAver = new int[nNumbers];
                int[] kSum = new int[nNumbers];
                int[] kTemp = new int[nNumbers];
                byte[] aSegmentMap = new byte[aByte];

                for (int i = 0; i < nNumbers; i++)
                {
                    //kNum[i] = 0;
                    //kSum[i] = 0;
                    kAver[i] = kOldAver[i] = Convert.ToInt16(i * 255 / (nNumbers - 1));
                }

                while (true)
                {
                    int order = 0;
                    for (int i = 0; i < nNumbers; i++)
                    {
                        kAver[i] = kOldAver[i];
                        kNum[i] = 0;
                        kSum[i] = 0;
                    }

                    for (int i = 0; i < aByte; i++)
                    {
                        for (int j = 0; j < nNumbers; j++)
                        {
                            kTemp[j] = Math.Abs(aGrayValues[i] - kAver[j]);
                        }
                        int temp = 255;

                        for (int j = 0; j < nNumbers; j++)
                        {
                            if (kTemp[j] < temp)
                            {
                                temp = kTemp[j];
                                order = j;
                            }
                        }
                        kNum[order]++;
                        kSum[order] += aGrayValues[i];
                        aSegmentMap[i] = Convert.ToByte(order);
                    }
                    for (int i = 0; i < nNumbers; i++)
                    {
                        if (kNum[i] != 0)
                            kOldAver[i] = Convert.ToInt16(kSum[i] / kNum[i]);
                    }

                    int kkk = 0;
                    for (int i = 0; i < nNumbers; i++)
                    {
                        if (kAver[i] == kOldAver[i])
                            kkk++;
                    }
                    if (kkk == nNumbers)
                        break;
                }

                for (int i = 0; i < aByte; i++)
                {
                    for (int j = 0; j < nNumbers; j++)
                    {
                        if (aSegmentMap[i] == j)
                        {
                            aGrayValues[i] = Convert.ToByte(kAver[j]);
                        }
                    }
                }
            }
            else //ISODATA
            {
                int k = 2 * nNumbers;
                byte[] segmentMap = new byte[aByte];
                List<int> kTemp = new List<int>();
                List<int> kNum = new List<int>();
                List<int> kAver = new List<int>();
                List<int> kSum = new List<int>();
                kAver.Clear();
                kNum.Clear();
                kTemp.Clear();
                kSum.Clear();
                for (int i = 0; i < k; i++)
                {
                    kAver.Add(Convert.ToInt16(i * 255 / (k - 1)));
                    kNum.Add(0);
                    kTemp.Add(0);
                    kSum.Add(0);
                }

                while (true)
                {
                    int nTemp;
                    for (int i = 0; i < aByte; i++)
                    {
                        kTemp.Clear();
                        int order = 0;
                        for (int j = 0; j < k; j++)
                        {
                            kTemp.Add(Math.Abs(aGrayValues[i] - kAver[j]));
                        }
                        nTemp = 255;

                        for (int j = 0; j < k; j++)
                        {
                            if (kTemp[j] < nTemp)
                            {
                                nTemp = kTemp[j];
                                order = j;
                            }
                        }
                        int num = kNum[order] + 1;
                        kNum.RemoveAt(order);
                        kNum.Insert(order, num);
                        int sum = kSum[order] + aGrayValues[i];
                        kSum.RemoveAt(order);
                        kSum.Insert(order, sum);
                        segmentMap[i] = Convert.ToByte(order);
                    }

                    for (int i = 0; i < k; i++)
                    {
                        if (kNum[i] == 0)
                        {
                            kNum.RemoveAt(i);
                            kAver.RemoveAt(i);
                            kSum.RemoveAt(i);
                            i--;
                            k--;
                        }
                    }

                    kAver.Clear();
                    for (int i = 0; i < k; i++)
                    {
                        kAver.Add(Convert.ToInt16(kSum[i] / kNum[i]));
                    }
                    if (k <= nNumbers)
                        break;
                    nTemp = 255;
                    int removeI = 0, removeJ = 0;
                    for (int i = 0; i < k; i++)
                    {
                        for (int j = i + 1; j < k; j++)
                        {
                            int distanceIJ = Math.Abs(kAver[i] - kAver[j]);
                            if (distanceIJ < nTemp)
                            {
                                nTemp = distanceIJ;
                                removeI = i;
                                removeJ = j;
                            }
                        }
                    }
                    k--;
                    kNum.Add(kNum[removeI] + kNum[removeJ]);
                    kAver.Add(Convert.ToInt16((kNum[removeI] * kAver[removeI] + kNum[removeJ] * kAver[removeJ]) / (kNum[removeI] + kNum[removeJ])));
                    kSum.Add(kNum[removeI] * kAver[removeI] + kNum[removeJ] * kAver[removeJ]);
                    kNum.RemoveAt(removeI);
                    kNum.RemoveAt(removeJ);
                    kAver.RemoveAt(removeI);
                    kAver.RemoveAt(removeJ);
                    kSum.RemoveAt(removeI);
                    kSum.RemoveAt(removeJ);
                }

                for (int i = 0; i < aByte; i++)
                {
                    for (int j = 0; j < nNumbers; j++)
                    {
                        if (segmentMap[i] == j)
                        {
                            aGrayValues[i] = Convert.ToByte(kAver[j]);
                        }
                    }
                }
            }

            Marshal.Copy(aGrayValues, 0, ptr, aByte);
            currentBitmap.Dispose(); //currentBitmap.UnlockBits(bmpData);
            return _bitmap;
        }

        /// <summary>
        /// This is used to clone a filter, each filter must implement it's own clone functionality
        /// </summary>
        /// <returns></returns>
        public override object Clone()
        {
            return new Clustering(this);
        }
    }
}
    

